/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.task.impl;

import elaborate.task.Task;
import elaborate.task.TaskQueue;
import elaborate.task.TaskQueueEvent;
import elaborate.task.TaskQueueEventListener;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author lendle
 */
public class DefaultTaskQueueImpl implements TaskQueue{
    private List<Task> queue=new ArrayList<Task>();
    private List<TaskQueueEventListener> listeners=new ArrayList<TaskQueueEventListener>();
    private TaskQueueThread thread=new TaskQueueThread();
    private static final int EVENT_ADDED=0, EVENT_COMPLETED=1, EVENT_FAILED=2, EVENT_EMPTIED=3, EVENT_STARTED=4, EVENT_STOPPED=5, EVENT_PAUSED=6, EVENT_RESUMED=7;
    @Override
    public void start() {
        thread.start();
    }

    @Override
    public void stop() {
        thread.setStatus(TaskQueueThread.STATUS_STOPPED);
    }

    @Override
    public void pause() {
        thread.setStatus(TaskQueueThread.STATUS_PAUSED);
    }

    @Override
    public void resume() {
        thread.setStatus(TaskQueueThread.STATUS_NORMAL);
    }

    @Override
    public int size() {
        return this.queue.size();
    }

    @Override
    public List<Task> getTasks() {
        return new ArrayList<Task>(this.queue);
    }

    @Override
    public synchronized void addTask(Task task) {
        this.queue.add(task);
        DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_ADDED, new TaskQueueEvent(task, DefaultTaskQueueImpl.this, null, null));
        this.notifyAll();
    }
    
    protected synchronized Task nextTask(){
        while(this.queue.isEmpty()){
            try {
                wait(500);
            } catch (InterruptedException ex) {
                Logger.getLogger(DefaultTaskQueueImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if(this.queue.isEmpty()){
            return null;
        }
        Task t=this.queue.remove(0);
        
        return t;
    }

    @Override
    public void addTaskQueueListener(TaskQueueEventListener l) {
        this.listeners.add(l);
    }

    @Override
    public void removeTaskQueueListener(TaskQueueEventListener l) {
        this.listeners.remove(l);
    }
    
    private void fireTaskQueueEvent(int eventType, TaskQueueEvent e){
        for(TaskQueueEventListener l : this.listeners){
            switch(eventType){
                case EVENT_ADDED:
                    l.taskAdded(e);break;
                case EVENT_COMPLETED:
                    l.taskCompleted(e);break;
                case EVENT_FAILED:
                    l.taskFailed(e);break;
                case EVENT_EMPTIED:
                    l.taskQueueEmptied(e);break;
                case EVENT_STARTED:
                    l.taskQueueStarted(e);break;
                case EVENT_STOPPED:
                    l.taskQueueStopped(e);break;
                case EVENT_PAUSED:
                    l.taskQueuePaused(e);break;
                case EVENT_RESUMED:
                    l.taskQueueResumed(e);
            };
        }
    }

    @Override
    public synchronized void addTask(Task task, boolean preempt) {
        if(preempt==false){
            this.addTask(task);
        }
        else{
            //preemption requested
            this.queue.add(0, task);
            DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_ADDED, new TaskQueueEvent(task, DefaultTaskQueueImpl.this, null, null));
            this.notifyAll();
        }
    }
    /**
     * an internal thread work as a task dispatcher
     * it is a daemon
     */
    class TaskQueueThread extends Thread
    {
        public static final int STATUS_PAUSED=0, STATUS_STOPPED=-1, STATUS_NORMAL=1;
        private int status=STATUS_NORMAL;
        public TaskQueueThread() {
            setDaemon(true);
        }

        public synchronized void setStatus(int status) {
            this.status = status;
            this.notifyAll();
        }

        public int getStatus() {
            return status;
        }
        
        
        public void run(){
            DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_STARTED, new TaskQueueEvent(null, DefaultTaskQueueImpl.this, null, null));
            synchronized(this){
                outer: while(true){
                    try{
                        boolean paused=false;
                        while(this.status!=STATUS_NORMAL){
                            //if status!=normal, wait for status change
                            DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_PAUSED, new TaskQueueEvent(null, DefaultTaskQueueImpl.this, null, null));
                            paused=true;
                            if(this.status==STATUS_STOPPED){
                                break outer;
                            }
                            wait();
                        }
                        if(paused){
                            //send notification of being resumed
                            DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_RESUMED, new TaskQueueEvent(null, DefaultTaskQueueImpl.this, null, null));
                        }
                        Task t=nextTask();
                        if(t!=null){
                            try{
                                Object result=t.run();
                                DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_COMPLETED, new TaskQueueEvent(t, DefaultTaskQueueImpl.this, null, result));
                            }catch(Exception e1){
                                e1.printStackTrace();
                                DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_FAILED, new TaskQueueEvent(t, DefaultTaskQueueImpl.this, e1, null));
                            }
                            if(DefaultTaskQueueImpl.this.queue.isEmpty()){
                                DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_EMPTIED, new TaskQueueEvent(null, DefaultTaskQueueImpl.this, null, null));
                            }
                        }
                    }catch(Exception e){e.printStackTrace();}
                }
            }
            DefaultTaskQueueImpl.this.fireTaskQueueEvent(DefaultTaskQueueImpl.EVENT_STOPPED, new TaskQueueEvent(null, DefaultTaskQueueImpl.this, null, null));
        }
    }
}
